/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#if !defined(AFX_TLATTICE_H__D32DD97D_BD03_4DBF_8B35_50B1E697A1E0__INCLUDED_)
#define AFX_TLATTICE_H__D32DD97D_BD03_4DBF_8B35_50B1E697A1E0__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

namespace contour
{
/*! 
	\defgroup LatticeGroup Lattices and mesh
	\ingroup ContourLibGroup
*/

/*! \brief Interface of the lattice objects

  \ingroup LatticeGroup
*/
template <class T>
class TLattice
{
public:
	//! \name Structures and Typedefs
	//@{
	//! a 2D vertex
	typedef std::pair<T,T> Vertex;
	//! a vector of Vertex
	typedef std::vector< Vertex> VertexContainer;
	//! a vector of Vertex index
	typedef std::vector<size_t> IndexContainer;
	//! a rectangle region
	struct Rect
	{
		T left;
		T right;
		T bottom;
		T top;
	};
	//@}

	//! \name Constructors
	//@{
	//! Default constructor
	TLattice():m_bModified(true){};
	virtual ~TLattice(){};
	//@}

	//! \name Bounding box
	//@{
	//! Computes the bounding box of the lattice
	virtual void GetBoundingBox(Rect& r)=0;
	//@}

	//! \name Modified flag
	//@{
	//! set the modified flag to true
	void SetModifiedFlag()				{	m_bModifiedFlag=true;};
	//! return true if modified
	bool IsModified() const				{	return m_bModified;};
	//@}

	//! \name vertex coordinates
	//@{
	//! returns the number of poitns
	virtual size_t GetVertexSize() const=0;
	//! returns the x coordinate of the i-th vertex
	virtual T GetXVertex(size_t i) const=0;
	//! returns the y coordinate of the i-th vertex
	virtual T GetYVertex(size_t i) const=0;
	//@}

	//! \name Faces
	//@{
	//! return the number of side per face
	virtual size_t GetFaceSideSize() const = 0;
	//! returns the number of faces
	virtual size_t GetFaceSize() const = 0;
	//! return the summits of the face
	virtual void GetFacePos( size_t iFace, VertexContainer& vVertex ) const=0;
	//! return the index summits of the face
	virtual void GetFaceIndex( size_t iFace, IndexContainer& vIndex ) const=0;
	//! return the index summits of the face
	void GetFace( size_t iFace, IndexContainer& vIndex, VertexContainer& vVertex ) const	
	{	
		GetFaceIndex(iFace,vIndex); 
		GetFacePos(iFace,vVertex); 
	};
	//@}
protected:
	//! \name Attributes
	//@{
	bool m_bModified;
	Rect m_rect;
	//@}
};

/*! \brief Scatter of point lattice, with no face

\ingroup LatticeGroup
*/
template<class T>
class TScatterLattice : public TLattice<T>
{
public:
	//! \name Vertex setter and getter
	//@{
	//! return the vertex container
	VertexContainer& GetVertices()					{	return m_vertices;};
	//! return the vertex container, const
	const VertexContainer& GetVertices() const		{	return m_vertices;};
	//! sets the vertex container
	void SetVertices( const VertexContainer& v)		{	m_vertices=v; SetModifiedFlag();};
	//@}

	//! \name Vertex coordinates
	//@{
	//! returns the number of poitns
	virtual size_t GetVertexSize() const				{	return m_vertices.size();};
	//! returns the x coordinate of the i-th vertex
	virtual T GetXVertex(size_t i) const				{	return m_vertices[i].first;};
	//! returns the y coordinate of the i-th vertex
	virtual T GetYVertex(size_t i) const				{	return m_vertices[i].second;};
	//@}

	//! \name Faces
	//@{
	//! return the number of side per face
	virtual size_t GetFaceSideSize() const		{	return 0;};
	//! returns the number of faces
	virtual size_t GetFaceSize() const			{	return 0;};
	//! return the summits of the face
	virtual void GetFacePos( size_t iFace, VertexContainer& vVertex ) const	{	vVertex.clear();};
	//! return the index summits of the face
	virtual void GetFaceIndex( size_t iFace, IndexContainer& vIndex ) const	{	vIndex.clear();};
	//@}
protected:
	//! \name Attributes
	//@{
	//! Vertices
	VertexContainer m_vertices;
	//@}
};

/*! \brief Triangular mesh

  \ingroup LatticeGroup
*/
template<class T>
class TTriMeshLattice : public TLattice<T>
{
public:
	//! \name Structures and typedefs
	//@{
	//! index of the triangle
	struct Face
	{
		//! index of the first vertex
		size_t m_p0;
		//! index of the second vertex
		size_t m_p1;
		//! index of the third vertex
		size_t m_p2;
	};
	//! vector of triangle corner index
	typedef std::vector<Face> FaceContainer;
	//@}

	//! \name Bounding box
	//@{
	//! Computes the bounding box of the lattice
	virtual void GetBoundingBox(Rect& r);
	//@}

	//! \name vertex coordinates
	//@{
	//! returns the number of vertices
	virtual size_t GetVertexSize() const	{	return m_vVertices.size();};
	//! returns the x coordinate of the i-th vertex
	virtual T GetXVertex(size_t i) const	{	ASSERT(i<m_vVertices.size()); return m_vVertices[i].first;};
	//! returns the y coordinate of the i-th vertex
	virtual T GetYVertex(size_t i) const	{	ASSERT(i<m_vVertices.size()); return m_vVertices[i].second;};
	//@}

	//! \name Faces and Vertices
	//@{
	//! Sets the vector of Vertices
	void SetVertices( const VertexContainer& vVertices)	{	m_vVertices = vVertices; SetModifiedFlag();};
	//! returns the vector of Vertices
	VertexContainer& GetVertices()							{	return m_vVertices;};
	//! returns the vector of Vertices, const
	const VertexContainer& GetVertices() const				{	return m_vVertices;};
	//! Sets the vector of triangle
	void SetFaces( const VertexContainer& vFaces)	{	m_vFaces = vFaces; SetModifiedFlag();};
	//! return the vector of faces index
	FaceContainer& GetFaces()						{	return m_vFaces;};
	//! return the vector of faces index, const
	const FaceContainer& GetFaces() const			{	return m_vFaces;};
	//@}

	//! \name Faces
	//@{
	//! return the number of side per face
	virtual size_t GetFaceSideSize() const		{	return 3;};
	//! returns the number of faces
	virtual size_t GetFaceSize() const			{	return m_vFaces.size();};	
	//! return the summits of the face
	virtual void GetFacePos( size_t iFace, VertexContainer& vPos ) const;
	//! return the index summits of the face
	virtual void GetFaceIndex( size_t iFace, IndexContainer& vIndex ) const;
	//@}

protected:
	//! \name Attributes
	//@{
	//! vertices description
	VertexContainer m_vVertices;
	//! Face description
	FaceContainer m_vFaces;
	//@}
};


template<class T>
void TTriMeshLattice<T>::GetBoundingBox(TLattice<T>::Rect& r)
{
	if (m_mVertices.size()==0)
		return;

	if (IsModified())
	{
		m_rect.left=m_vVertices[0].first;
		m_rect.right=m_vVertices[0].first;
		m_rect.bottom=m_vVertices[0].second;
		m_rect.top=m_vVertices[0].second;

		for (size_t i=1;i<m_vVertices.size();i++)
		{
			m_rect.left=__min(r.left, m_vVertices[i].first);
			m_rect.right=__min(r.right, m_vVertices[i].first);
			m_rect.bottom=__min(r.bottom, m_vVertices[i].second);
			m_rect.top=__min(r.top, m_vVertices[i].second);
		}

		m_bModified=false;
	}
	r=m_rect;
};

template<class T>
void TTriMeshLattice<T>::GetFacePos(size_t iFace, VertexContainer& vPos ) const
{
	ASSERT(iFace < GetFaceSize());
	ASSERT( m_vFaces[iFace].m_p0 < GetVertexSize());
	ASSERT( m_vFaces[iFace].m_p1 < GetVertexSize());
	ASSERT( m_vFaces[iFace].m_p2 < GetVertexSize());

	vPos.resize(3);
	vPos[0]=m_vVertices[ m_vFaces[iFace].m_p0];
	vPos[1]=m_vVertices[ m_vFaces[iFace].m_p1];
	vPos[2]=m_vVertices[ m_vFaces[iFace].m_p2];
}

template<class T>
void TTriMeshLattice<T>::GetFaceIndex(size_t iFace, IndexContainer& vIndex ) const
{
	ASSERT(iFace < GetFaceSize());

	vIndex.resize(3);
	vIndex[0]=m_vFaces[iFace].m_p0;
	vIndex[1]=m_vFaces[iFace].m_p1;
	vIndex[2]=m_vFaces[iFace].m_p2;
}

/*! \brief Regular grid of quads

  \ingroup LatticeGroup
*/
template<class T>
class TQuadGridLattice : public TLattice<T>
{
public:
	//! \name Rows and columns count
	//@{
	//! return the number of columns
	virtual size_t GetColSize() const=0;
	//! return the number of rows
	virtual size_t GetRowSize() const=0;
	//@}

	//! \name Faces
	//! return the number of side (4)
	size_t GetFaceSideSize() const										{	return 4;};
	//! returns the number of faces
	virtual size_t GetFaceSize() const									{	return (GetColSize()-1)*(GetRowSize()-1);};
	//! return the summits of the face
	virtual void GetFacePos( size_t iFace, VertexContainer& vFace ) const;
	//! return the index summits of the face
	virtual void GetFaceIndex( size_t iFace, IndexContainer& vFace ) const;
	//@}

protected:
	//! Compute the index of the upper-left vertex of the face iFace
	size_t GetUpperLeftvertexIndexFromFaceIndex(size_t iFace) const	
	{	
		size_t colSize=GetColSize();
		return iFace/(colSize-1)*colSize+iFace%(colSize-1);
	};
};


template<class T>
void TQuadGridLattice<T>::GetFacePos( size_t iFace, TLattice<T>::VertexContainer& vFace ) const
{
	iFace= __min( GetFaceSize()-1, iFace);
	size_t i;

	vFace.resize(4);
	// first line
	i=GetUpperLeftvertexIndexFromFaceIndex(iFace);
	vFace[0].first=GetXVertex(i);		vFace[0].second=GetYVertex(i);
	i++;	
	vFace[1].first=GetXVertex(i);		vFace[1].second=GetYVertex(i);
	i+=GetColSize();
	vFace[2].first=GetXVertex(i);		vFace[2].second=GetYVertex(i);
	i--;	
	vFace[3].first=GetXVertex(i);		vFace[3].second=GetYVertex(i);

};

template<class T>
void TQuadGridLattice<T>::GetFaceIndex( size_t iFace, TLattice<T>::IndexContainer& vFace ) const
{
	iFace= __min( GetFaceSize()-1, iFace);
	size_t i;

	vFace.resize(4);
	// first line
	i=GetUpperLeftvertexIndexFromFaceIndex(iFace);
	vFace[0]=i;
	i++;	
	vFace[1]=i;
	i+=GetColSize();
	vFace[2]=i;
	i--;	
	vFace[3]=i;
};

/*! \brief Linearly spaced lattice of quads

  \ingroup LatticeGroup
*/
template <class T>
class TQuadLinGridLattice : public TQuadGridLattice<T>
{
public:
	//! \name Constructor
	//@{
	/*! \brief Default constructor

	\param uNy number of vertex rows, default is 2
	\param uNx number of vertex columns, default is 2,
	\param tLeft x coordinate of the first vertex column, default is 0,
	\param tTop y coordinate of the first vertex row, default is 1,
	\param tDx spatial step between vertex columns, default is 1,
	\param tDy spatial step between vertex rows, default is 1
	*/
	TQuadLinGridLattice(size_t uNy=2, size_t uNx=2, T tLeft= 0, T tTop = 1, T tDx = 1, T tDy = 1)
		: m_uNx(uNx), m_uNy(uNy), m_tLeft(tLeft), m_tTop(tTop), m_tDx(tDx), m_tDy(tDy){};
	virtual ~TQuadLinGridLattice(){};
	//@}

	//! \name Regular lattice settings...
	//@{
	//! sets the number of vertex columns
	void SetColSize(size_t uColSize)					{	m_uNx = __max(2, uColSize); SetModifiedFlag();};
	//! sets the number of vertex rows
	void SetRowSize(size_t uRowSize)					{	m_uNy = __max(2, uRowSize); SetModifiedFlag();};
	//! sets the spatial step between vertex columns
	void SetColStep(T tColStep)						{	m_tDx = uColStep; SetModifiedFlag();};
	//! sets the spatial step between vertex rows
	void SetRowStep(T tRowStep)						{	m_tDy = uRowStep; SetModifiedFlag();};
	//! sets x coordinate of the first column
	void SetLeft(T tLeft)							{	m_tLeft = tLeft; SetModifiedFlag();};
	//! sets y coordinate of the first row
	void SetTop(T tTop)								{	m_tTop=tTop; SetModifiedFlag();};
	//! sets the lattice bounding box
	void SetBoundingBox( T tpx1, T tpy1, T tpx2, T tpy2);
	//! return the number of vertex columns
	size_t GetColSize() const							{	return m_uNx;};
	//! return the number of vertex rows
	size_t GetRowSize() const							{	return m_uNy;};
	//! return the spatial step between vertex columns
	T GetColStep() const							{	return m_tDx;};
	//! return the spatial step between vertex rows
	T GetRowStep() const							{	return m_tDy;};
	T GetWidth() const								{	return (m_uNx-1)*m_tDx;};
	T GetHeight() const								{	return (m_uNy-1)*m_tDy;};
	T GetLeft() const								{	return m_tLeft;};
	T GetTop() const								{	return m_tTop;};
	T GetBottom() const								{	return m_tTop-GetHeight();};
	T GetRight() const								{	return m_tLeft+GetWidth();};
	//@}

	//! \name Bounding box
	//@{
	void GetBoundingBox(Rect& r);
	//@}

	//! \name vertex coordinates
	//@{
	//! returns the number of Vertices
	virtual size_t GetVertexSize() const				{	return m_uNy*m_uNx;};
	//! return the x coordinate of the i-th vertex
	virtual T GetXVertex(size_t i) const				{	return m_tLeft+(i%m_uNx)*m_tDx;};
	//! return the y coordinate of the i-th vertex
	virtual T GetYVertex(size_t i) const				{	return m_tTop-(i/m_uNx)*m_tDy;};
	//@}
protected:
	//! \name Attributes
	//@{
	//! number of vertex columns
	size_t m_uNx;
	//! number of vertex rows
	size_t m_uNy;
	T m_tLeft;
	T m_tTop;
	T m_tDx;
	T m_tDy;
	//@}
};


template<class T>
void TQuadLinGridLattice<T>::GetBoundingBox(TLattice<T>::Rect& r )
{
	if (IsModified())
	{
		m_rect.left=GetLeft();
		m_rect.top=GetTop();
		m_rect.right=GetRight();
		m_rect.bottom=GetBottom();

		m_bModified=false;
	}
	r=m_rect;
}


template<class T>
void TQuadLinGridLattice<T>::SetBoundingBox( T tpx1, T tpy1, T tpx2, T tpy2)
{
	m_tLeft = __min(tpx1, tpx2);
	m_tTop = __min(tpy1, tpy2);

	m_tDx=fabs(tpx1-tpx2)/(m_uNx-1);
	m_tDy=fabs(tpy1-tpy2)/(m_uNx-1);

	SetModifiedFlag();
};

/*! \brief Quad Regular lattice

  \ingroup LatticeGroup
*/
template<class T>
class TQuadRegGridLattice : public TQuadGridLattice<T>
{
public:
	//! \name Typedefs
	//@{
	typedef std::vector<T> TContainer;
	//@}

	//! \name Grid methods
	//@{
	//! return the x coordinates container, const
	const TContainer& GetXVertices() const			{	return m_vXVertices;};
	//! return the x coordinates container
	TContainer& GetXVertices()						{	return m_vXVertices;};
	//! return the y coordinates container, const
	const TContainer& GetYVertices() const			{	return m_vYVertices;};
	//! return the y coordinates container
	TContainer& GetYVertices()						{	return m_vYVertices;};
	//! sets a x Vertex,
	void SetXVertex( const TContainer& vX)			{	m_vXVertices=vX;};
	//! sets a y Vertex,
	void SetYVertex( const TContainer& vY)			{	m_vYVertices=vY;};
	//@}

	//! \name Grid dimension
	//@{
	//! return the number of columns
	virtual size_t GetColSize() const					{	return m_vXVertices.size();};
	//! return the number of rows
	virtual size_t GetRowSize() const 				{	return m_vYVertices.size();};
	//@}

	//! \name Bounding box
	//@{
	void GetBoundingBox(Rect& r);
	//@}

	//! \name vertex coordinates
	//@{
	//! returns the number of Vertices
	virtual size_t GetVertexSize() const				{	return m_vXVertices.size()*m_vYVertices.size();};
	//! return the x coordinate of the i-th vertex
	virtual T GetXVertex(size_t i) const				{	ASSERT(i%GetColSize()<m_vXVertices.size()); return m_vXVertices[i%GetColSize()];};
	//! return the y coordinate of the i-th vertex
	virtual T GetYVertex(size_t i) const				{	ASSERT(i/GetColSize()<m_vYVertices.size()); return m_vYVertices[i/GetColSize()];};
	//@}
protected:
	//! \name Attributes
	//@{
	TContainer m_vXVertices;
	TContainer m_vYVertices;
	//@}
};

template<class T>
void TQuadRegGridLattice<T>::GetBoundingBox(TLattice<T>::Rect& r)
{
	if (IsModified())
	{
		m_rect.left=m_vXVertices[0];
		m_rect.right=m_vXVertices[m_vXVertices.size()-1];
		m_rect.top=m_vYVertices[0];
		m_rect.bottom=m_vYVertices[m_vYVertices.size()-1];

		m_bModified=false;
	}
	r=m_rect;
}

/*! Regualar mesh of quads

  \ingroup LatticeGroup
*/
template<class T>
class TQuadMeshGridLattice : public TQuadGridLattice<T>
{
public:
	//! \name Typedefs
	//@{
	//! Matrix of vertices
	typedef std::TSTLMatrix<Vertex> VertexMatrix;
	//@}

	//! \name Grid dimension
	//@{
	//! return the number of columns
	virtual size_t GetColSize() const					{	return m_mVertices.ncols();};
	//! return the number of rows
	virtual size_t GetRowSize() const 				{	return m_mVertices.nrows();};
	//@}

	//! \name Bounding box
	//@{
	void GetBoundingBox(Rect& r);
	//@}

	//! \name vertex coordinates
	//@{
	//! returns the number of Vertices
	virtual size_t GetVertexSize() const				{	return m_mVertices.size();};
	//! return the x coordinate of the i-th vertex
	virtual T GetXVertex(size_t i) const				{	ASSERT(i<m_mVertices.size()); return m_mVertices[i].first;};
	//! return the y coordinate of the i-th vertex
	virtual T GetYVertex(size_t i) const				{	ASSERT(i<m_mVertices.size()); return m_mVertices[i].second;};
	//@}

	//! \name Mesh methods
	//@{
	//! return the vertex coordinates, const
	const VertexMatrix& GetVertices() const					{	return m_mVertices;};
	//! return the vertex coordinates
	VertexMatrix& GetVertices()								{	return m_mVertices;};
	//@}
		
protected:
	VertexMatrix m_mVertices;
};

template<class T>
void TQuadMeshGridLattice<T>::GetBoundingBox(TLattice<T>::Rect& r)
{
	if (m_mVertices.size()==0)
		return;

	if (IsModified())
	{
		m_rect.left=m_mVertices[0].first;
		m_rect.right=m_mVertices[0].first;
		m_rect.bottom=m_mVertices[0].second;
		m_rect.top=m_mVertices[0].second;

		for (size_t i=1;i<m_mVertices.size();i++)
		{
			m_rect.left=__min(r.left, m_mVertices[i].first);
			m_rect.right=__min(r.right, m_mVertices[i].first);
			m_rect.bottom=__min(r.bottom, m_mVertices[i].second);
			m_rect.top=__min(r.top, m_mVertices[i].second);
		}

		m_bModified=false;
	}
	r=m_rect;
};

};
#endif // !defined(AFX_TLATTICE_H__D32DD97D_BD03_4DBF_8B35_50B1E697A1E0__INCLUDED_)
